package com.boyan.auth.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.bean.copier.CopyOptions;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.boyan.auth.domain.dto.LoginFormDTO;
import com.boyan.auth.domain.dto.UserFomDTO;
import com.boyan.auth.domain.holder.UserHolder;
import com.boyan.auth.domain.vo.UserVO;
import com.boyan.auth.mapper.UserMapper;
import com.boyan.auth.service.IUserService;
import com.boyan.auth.domain.po.User;
import com.boyan.pojo.Result;
import com.boyan.utils.JwtUtils;
import com.boyan.utils.RedisConstants;
import com.boyan.utils.RegexUtils;
import jakarta.servlet.http.HttpSession;
import jakarta.validation.constraints.Size;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import static com.boyan.utils.RedisConstants.*;
import static com.boyan.utils.SystemConstants.USER_NICK_NAME_PREFIX;


@Service
@RequiredArgsConstructor
@Slf4j
public class UserServiceImpl  extends ServiceImpl<UserMapper, User> implements IUserService{

    private final StringRedisTemplate stringRedisTemplate;

    private final RedisTemplate<String, String> redisTemplate;



    @Override
    public void deduction(Long id, BigDecimal money) {
        User user = getById(id);
        if(user.getStatus()==2){
            throw new RuntimeException("用户状态异常");
        }

        if (user.getBalance().compareTo(money) < 0) {
            throw new RuntimeException("余额不足");
        }

        user.setBalance(user.getBalance().subtract(money));

        updateById(user);
    }

    @Override
    public List<UserVO> queryByCondition(String username, Integer status, BigDecimal minBalance, BigDecimal maxBalance) {
        List<User> users = lambdaQuery()
                .eq(status != null, User::getStatus, status)
                .like(username != null, User::getUsername, username)
                .ge(minBalance != null, User::getBalance, minBalance)
                .le(maxBalance != null, User::getBalance, maxBalance)
                .list();
        return users.stream()
                .map(
                        user->{
//                            UserVO userVO = new UserVO();
//                            BeanUtils.copyProperties(user,userVO);
//                            return userVO;
                            return BeanUtil.copyProperties(user,UserVO.class);
                        }
                )
                .collect(Collectors.toList());

    }

    @Override
    public Result sendCode(String phone, HttpSession session) {
        if(RegexUtils.isPhoneInvalid(phone)){
            return Result.error("手机号格式错误");
        }

        String code= RandomUtil.randomNumbers(6);

        stringRedisTemplate.opsForValue().set(LOGIN_CODE_KEY +phone,code,LOGIN_CODE_TTL, TimeUnit.MINUTES);

        log.debug("发送验证码成功，验证码：{}",code);


        return Result.success(code);
    }

    @Override
    public Result login(LoginFormDTO loginForm, HttpSession session) {
        String phone = loginForm.getPhone();
        if(RegexUtils.isPhoneInvalid(phone)){
            return Result.error("手机号格式错误");
        }

        //TODO 从redis获取验证码进行校验
        String cacheCode = stringRedisTemplate.opsForValue().get(LOGIN_CODE_KEY + phone);
        String code = loginForm.getCode();
        if(cacheCode == null || !cacheCode.equals(code)){
            return Result.error("验证码错误");
        }
        //一致，根据手机号查询用户
        User user = query().eq("phone", phone).one();
        if(user == null){
            user=createUserWithPhone(phone);
        }

        //保存用户到redis
        //1.随机生成token，作为登陆令牌
        //2.将user对象转为hash存储
        //3.存储
        String token = JwtUtils.generateToken(Map.of("phone",user.getPhone()));
        //log.info("token:{}",token);

        //将user转成HashMap
        UserFomDTO userDTO=BeanUtil.copyProperties(user,UserFomDTO.class);
//        Map<String,Object> userMap = BeanUtil.beanToMap(userDTO,new HashMap<>(),
//                CopyOptions.create()
//                .setIgnoreNullValue(true)
//                .setFieldValueEditor((fieldName, fieldValue) -> fieldValue.toString()));
        Map<String, Object> userMap = BeanUtil.beanToMap(userDTO, new HashMap<>(),
                CopyOptions.create().setIgnoreNullValue(true)
                        .setFieldValueEditor((fieldName, fieldValue) ->
                                fieldValue != null ? fieldValue.toString() : null));

        //存储
        String tokenKey = LOGIN_USER_KEY+token;
        stringRedisTemplate.opsForHash().putAll(tokenKey,userMap);
        //设置token有效期
        stringRedisTemplate.expire(tokenKey,LOGIN_USER_TTL,TimeUnit.MINUTES);

        return Result.success(token);
    }

    @Override
    public Result getUserById(Long id) {

        String userKey = CACHE_USER_KEY + id;
        String userJson = redisTemplate.opsForValue().get(userKey);
        log.info("userJson:{}",userJson);
        if(StrUtil.isNotBlank(userJson)){
            User user = JSONUtil.toBean(userJson, User.class);
            log.info("user:{}",user);
            return Result.success(user);
        }
        User user = getById(id);
        if(user == null){
            return Result.error("用户不存在");
        }
        redisTemplate.opsForValue().set(userKey, JSONUtil.toJsonStr(user));

        return Result.success(user);


    }


    private User createUserWithPhone(String phone) {

        User user = new User();
        user.setPhone(phone);
        user.setStatus(1);
        user.setName(USER_NICK_NAME_PREFIX+RandomUtil.randomString(10));
        //插入数据库
        save(user);

        return user;
    }

}
